package com.linb.pan.server.common.stream.consumer.file;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.linb.pan.server.common.stream.channel.PanChannels;
import com.linb.pan.server.common.stream.event.file.FilePhysicalDeleteEvent;
import com.linb.pan.server.common.stream.event.log.ErrorLogEvent;
import com.linb.pan.server.modules.file.entity.BPanFile;
import com.linb.pan.server.modules.file.entity.BPanUserFile;
import com.linb.pan.server.modules.file.enums.FolderFlagEnum;
import com.linb.pan.server.modules.file.service.IFileService;
import com.linb.pan.server.modules.file.service.IUserFileService;
import com.linb.pan.storage.engine.core.StorageEngine;
import com.linb.pan.storage.engine.core.context.DeleteFileContext;
import com.linb.pan.stream.core.AbstractConsumer;
import com.linb.pan.stream.core.IStreamProducer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
public class FilePhysicalDeleteEventConsumer extends AbstractConsumer {

    @Autowired
    private IFileService fileService;

    @Autowired
    private IUserFileService userFileService;

    @Autowired
    private StorageEngine storageEngine;

    @Autowired
    @Qualifier(value = "defaultStreamProducer")
    private IStreamProducer producer;

    @StreamListener(PanChannels.PHYSICAL_DELETE_FILE_INPUT)
    @Async(value = "eventListenerTaskExecutor")
    public void physicalDeleteFile(Message<FilePhysicalDeleteEvent> message) {
        if (isEmptyMessage(message)) {
            return;
        }
        printLog(message);
        FilePhysicalDeleteEvent event = message.getPayload();
        log.info("事件监听-准备彻底删除:" + event.getAllRecords());
        //1.查询无引用的实体文件记录
        List<BPanUserFile> allRecords = event.getAllRecords();
        if (CollectionUtils.isEmpty(allRecords)) {
            return;
        }
        List<Long> realFileIdList = findAllUnusedRealFileIdList(allRecords);
        List<BPanFile> realFileRecords = fileService.listByIds(realFileIdList);
        if (CollectionUtils.isEmpty(realFileRecords)) {
            return;
        }
        //2.删除记录
        if (!fileService.removeByIds(realFileIdList)) {
            producer.sendMessage(PanChannels.ERROR_LOG_OUTPUT,
                    new ErrorLogEvent("文件实体记录: " + JSON.toJSONString(realFileIdList) + " 删除失败", 0L));
            return;
        }
        //3.物理删除
        physicalDeleteFileByStorageEngine(realFileRecords);
    }

    /**
     * 查询无引用的实体文件记录
     */
    private List<Long> findAllUnusedRealFileIdList(List<BPanUserFile> allRecords) {
        List<Long> realFileIdList = allRecords.stream()
                // 必须是文件而不是文件夹
                .filter(r -> FolderFlagEnum.NO.getCode().equals(r.getFolderFlag()))
                // 必须是未关联的
                .filter(this::isUnused)
                .map(BPanUserFile::getRealFileId)
                .collect(Collectors.toList());
        return realFileIdList;
    }

    /**
     * 判断是否被引用
     */
    private boolean isUnused(BPanUserFile record) {
        QueryWrapper<BPanUserFile> queryWrapper = Wrappers.query();
        queryWrapper.eq("real_file_id", record.getRealFileId());
        // 当b_pan_file有，但b_pan_user_file没有时，视为废弃
        return userFileService.count(queryWrapper) == 0;
    }

    /**
     * 物理删除
     */
    private void physicalDeleteFileByStorageEngine(List<BPanFile> realFileRecords) {
        List<String> realPathList = realFileRecords.stream().map(BPanFile::getRealPath).collect(Collectors.toList());
        DeleteFileContext context = new DeleteFileContext();
        context.setRealFilePathList(realPathList);
        try {
            storageEngine.delete(context);
        } catch (IOException e) {
            ErrorLogEvent event = new ErrorLogEvent("实体文件idList: " + JSON.toJSONString(realPathList) + " 删除失败", 0l);
            producer.sendMessage(PanChannels.ERROR_LOG_OUTPUT, event);
        }
    }

}
